package com.genius.user.service.impl;

import com.genius.user.bean.login.LoginForm;
import com.genius.user.bean.login.PhoneForm;
import com.genius.util.Encryption.JWTEncryption;
import com.genius.util.Encryption.MD5Encryption;
import com.genius.util.Exception.illuminatorStatus;
import com.genius.util.Random.SeniorRandom;
import com.genius.util.Utils.BeanUtils.BeanLab;
import com.genius.util.Utils.DateHelper;
import com.genius.util.Utils.RedisUtil;
import com.genius.util.Utils.Regex.RegexUtils;
import com.genius.util.Utils.VerifyTools;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.genius.user.bean.User;
import com.genius.user.bean.dto.UserDTO;
import com.genius.user.bean.login.RegisterForm;
import com.genius.user.mapper.UserMapper;
import com.genius.user.service.UserService;
import com.genius.util.Common.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

import static com.genius.util.ConstPool.RedisConstPool.*;


@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    UserMapper userMapper;

    @Override
    public UserDTO getUserInfo(String token) {
        String key = LOGIN_USERNAME_KEY  + token;
        String userDtoJson;
        UserDTO userDTO = null;
        if((userDtoJson=stringRedisTemplate.opsForValue().get(key))!=null){
            try {
                userDTO =  BeanLab.jsonToBean(UserDTO.class,userDtoJson);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return userDTO;
    }

    @Override
    public Result userRegister(RegisterForm registerForm) {
        String username = registerForm.getUsername();
        String phone=registerForm.getPhoneNumber();
        String email=registerForm.getEmail();
        logger.info("用户:{}，手机号：{} 开始进行注册",username,phone);

        Result result = this.verifyRegisterForm(username,email, phone,registerForm.getCode());

        if(Optional.ofNullable(result).isPresent()){
            return result;
        }

        User user = new User(new SeniorRandom().nextString(),username,MD5Encryption.Encrypte(registerForm.getPassword()),
                registerForm.getAvatar(),registerForm.getNickname(),email,DateHelper.getToday(),registerForm.getPhoneNumber());
        //注册用户
        save(user);
        //分发token
        logger.info("用户:{} 注册成功",username);
        return Result.success(GenerateUserToken(user));
    }

    @Override
    public Result sendCode(String phone) {
        // 1.校验手机号
        if (RegexUtils.isPhoneInvalid(phone)) {
            // 2.如果不符合，返回错误信息
            return Result.error("403","phoneNumber error");
        }
        String key = LOGIN_PHONE_KEY + phone;
        logger.info("手机号码：{} 请求验证码服务",phone);
        if (stringRedisTemplate.opsForValue().get(key)!=null) {
            return Result.error("403","code already send");
        }
        // 3.符合，生成验证码
        String code = new SeniorRandom().nextString('0','9',6);
        // 4.保存验证码到Redis
        if (!Optional.ofNullable(stringRedisTemplate.opsForValue().setIfAbsent(key, code, LOGIN_CODE_TTL, TimeUnit.MINUTES)).orElse(false)) {
            return Result.error("403","code already send");
        }

        //TODO  5.发送验证码api
        logger.info("发送短信验证码成功，验证码：{}", code);
        return Result.success();
    }


    @Override
    public Result login(LoginForm loginForm) {
        String username = loginForm.getUsername();
        String password = loginForm.getPassword();

        if (verifyReentrantLogin(username)) {
            return Result.error(illuminatorStatus.LoginError);
        }
        User temp = query().eq("username",username).one();
        if(temp==null){
            logger.info("用户不存在");
            return Result.error(illuminatorStatus.LoginInvalidUser);
        }else if(temp.getPassword().equals("")){
            logger.info("用户:{} 是手机注册用户暂未绑定密码",temp.getUsername());
            return Result.error(illuminatorStatus.LoginPhoneUserNoBindPassword);
        }
        else if(!temp.getPassword().equals(MD5Encryption.Encrypte(password))){
            logger.info("密码错误");
            return Result.error(illuminatorStatus.LoginPasswordError);
        }
        else{
            return Result.success(GenerateUserToken(temp));
        }
    }

    @Override
    public Result phoneLogin(PhoneForm loginForm) {
        String phone = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.error("403","Phone Number Error");
        }
        logger.info("手机号:{} 验证码登录",phone);
        if (!VerifyTools.verifyPhoneCodeByLua(phone,loginForm.getCode(),stringRedisTemplate)){
            logger.info("手机号不存在或者验证码错误");
            return Result.error(illuminatorStatus.PhoneCodeError);
        }
        User user = query().eq("phone_number", phone).one();
        if (user!=null&&verifyReentrantLogin(user.getUsername())) {
            logger.info("用户:{} 已经登录",user.getUsername());
            return Result.error(illuminatorStatus.LoginError);
        }
        if(user==null){
            user = createUserWithPhone(phone);
            logger.info("手机号:{} 第一次登录，为其创建账号:{}",phone,user);
        }
        return Result.success(GenerateUserToken(user));
    }

    /**
     * 检测手机验证码，以及邮箱，手机号，用户名是否已存在
     * @param username
     * @param email
     * @param phone
     * @param code
     * @return
     */
    private Result verifyRegisterForm(String username,String email,String phone,String code){
        if (!VerifyTools.verifyPhoneCodeByLua(phone,code,stringRedisTemplate)) {
            logger.info("验证码无效");
            return Result.error(illuminatorStatus.PhoneCodeError);
        }
        User temp = query().eq("username",username).one();
        if(temp!=null){
            logger.info("用户已经注册");
            return Result.error(illuminatorStatus.RegisterExistUser);
        }
        else if(!email.trim().equals("")){
            if(query().eq("email",email).count()!=0){
                logger.info("邮箱:{}已经被注册",email);
                return Result.error(illuminatorStatus.BindExistInfo);
            }

        }
        else if(!phone.trim().equals("")){
            if(query().eq("phone_number",phone).count()!=0){
                logger.info("手机号码:{}已经被注册",phone);
                return Result.error(illuminatorStatus.BindExistInfo);
            }
        }
        return null;
    }


    /**
     * 检查Redis中 LoginKey + username，判断用户是否重复登录
     * @param username
     * @return
     */
    private boolean verifyReentrantLogin(String username){
        if (stringRedisTemplate.opsForValue().get(LOGIN_USERNAME_KEY + username) != null) {
            logger.info("{} 用户已经登录",username);
           return true;
        }
        return false;
    }

    private Map<String,Object> GenerateUserToken(User user){
        String userId=user.getUserId();
        String username = user.getUsername();
        String token = JWTEncryption.GenerateToken(username,userId);

        //数据脱敏
        UserDTO userDTO = BeanLab.switchSoul(user,UserDTO.class);
        String userJson = BeanLab.beanToJson(userDTO);

        //RedisKey同步
        RedisUtil.saveRedis(userJson,LOGIN_USERNAME_KEY + token,LOGIN_TOKEN_TTL,TimeUnit.HOURS,stringRedisTemplate);
        return Map.of("token",token,"User",userDTO);
    }

    private User createUserWithPhone(String phone){
        User user = new User(new SeniorRandom().nextString(),phone,"",
                null,phone,null, DateHelper.getToday(),phone);

        save(user);
        return user;
    }

}
